package points

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"sale-admin/config"
	"sale-admin/config/mysql"
	"sale-admin/config/redis"
	"sale-admin/internal/app/grpc/client"
	"sale-admin/internal/app/grpc/protoc/devicec"
	"sale-admin/internal/dao"
	"sale-admin/internal/define"
	"sale-admin/internal/models"
	"strconv"
	"time"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"gorm.io/gorm"
)

type WinPoints struct {
	PointsInterface
}

const (
	WIN_SUCCESS = "PUB-00000"
)

// 3 会员查询
type WinMemberQueryReq WinCommonReq
type WinMemberQueryRsp struct {
	Code int          `json:"code"` // 200成功，其他失败
	Msg  string       `json:"msg"`
	Data MemberDetail `json:"data"`
}
type MemberDetail struct {
	ID        int     `json:"id"`         // 用户ID
	NickName  string  `json:"nickname"`   // 昵称
	Avatar    string  `json:"avatar"`     // 头像
	Mobile    string  `json:"mobile"`     // 手机号
	Gender    int     `json:"gender"`     // 性别
	Sn        string  `json:"sn"`         // 赢商会员卡号
	Point     float64 `json:"point"`      // 积分
	OpenID    string  `json:"open_id"`    // 微信OpenID
	CreatedAt string  `json:"created_at"` // 创建时间
}

// 获取机器对接的积分系统信息
func (WinPoints) GetRobotLocationInfo1(ctx context.Context, db *gorm.DB, salesID string) (res *devicec.Location, err error) {
	// 机器信息
	RobotInfo, _ := dao.Robot{}.RobotDetail(ctx, db, "", "", salesID)
	if RobotInfo == nil || RobotInfo.ID == "" {
		err = errorx.New("机器信息不存在", -1)
		return
	}
	// 初始化GRPC
	deviceClient, clientErr := client.GetDeviceAdminClient()
	if clientErr != nil {
		logx.Error(context.Background(), "grpc device client error", logx.Err(clientErr))
		err = errorx.New("系统错误", -1)
		return
	}

	// GRPC获取商场位置列表
	deviceReq := &devicec.LocationDetailReq{}
	deviceReq.LocationId = strconv.Itoa(RobotInfo.Areas3)
	deviceRes, err := deviceClient.LocationDetail(context.Background(), deviceReq)
	if deviceRes != nil && deviceRes.Code == 0 && err == nil {
		return deviceRes.Location, nil
	}

	return
}

func (WinPoints) GetSign(ctx context.Context, db *gorm.DB, SalesID string) (mallid, AccessToken string) {
	// 定义每个MallID对应的AccessToken
	accessTokenMap := make(map[string]string)
	// 前三个是正式的，第四个是测试的
	accessTokenMap["3735"] = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hLnpoaW1hdGVjaC5jb21cL3Jlc3RmdWxcL21hbGxcLzM3MzVcL3Rva2VuIiwiaWF0IjoxNzA5ODYwMTg3LCJleHAiOjk1OTM4NjAxODcsIm5iZiI6MTcwOTg2MDE4NywianRpIjoiRzRLdFlZcE5JSjg4bGxFaCIsInN1YiI6MzczNSwicHJ2IjoiYjU1ZTBmMDk2NDhlOTFlOTc4MTI1MDgzZWUyY2FkNTAwOTlhYzNmZiIsImd1YXJkIjoibWFsbCIsIm9yaWdpbiI6Ilx1ODI5ZFx1OWViYlx1NzUxZlx1NjIxMCJ9.F4R19gE2UwJzNNlvGYMW-OesUn2nZWT5Al9o0I7j1U8"
	accessTokenMap["3734"] = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hLnpoaW1hdGVjaC5jb21cL3Jlc3RmdWxcL21hbGxcLzM3MzRcL3Rva2VuIiwiaWF0IjoxNzA5ODYwNzA5LCJleHAiOjk1OTM4NjA3MDksIm5iZiI6MTcwOTg2MDcwOSwianRpIjoiY2dSTjJBSlR5ZjlRTTJjOCIsInN1YiI6MzczNCwicHJ2IjoiYjU1ZTBmMDk2NDhlOTFlOTc4MTI1MDgzZWUyY2FkNTAwOTlhYzNmZiIsImd1YXJkIjoibWFsbCIsIm9yaWdpbiI6Ilx1ODI5ZFx1OWViYlx1NzUxZlx1NjIxMCJ9.k5fYZuy7EXPh3QmqnuYkcWjKY46RinuWRaBYMvMkNRU"
	accessTokenMap["3733"] = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hLnpoaW1hdGVjaC5jb21cL3Jlc3RmdWxcL21hbGxcLzM3MzNcL3Rva2VuIiwiaWF0IjoxNzA5ODYwNzYxLCJleHAiOjk1OTM4NjA3NjEsIm5iZiI6MTcwOTg2MDc2MSwianRpIjoib0NOTlRoMFYyY3BJY1JCVyIsInN1YiI6MzczMywicHJ2IjoiYjU1ZTBmMDk2NDhlOTFlOTc4MTI1MDgzZWUyY2FkNTAwOTlhYzNmZiIsImd1YXJkIjoibWFsbCIsIm9yaWdpbiI6Ilx1ODI5ZFx1OWViYlx1NzUxZlx1NjIxMCJ9.HomIFpeJiZMGWtBUFZSdII7Unpp_jTEWRAXv_opKRDs"
	accessTokenMap["2379"] = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC90ZXN0YS56aGltYXRlY2guY29tXC9yZXN0ZnVsXC9tYWxsXC8yMzc5XC90b2tlbiIsImlhdCI6MTYyOTk1MTMxNywiZXhwIjo5NTEzOTUxMzE3LCJuYmYiOjE2Mjk5NTEzMTcsImp0aSI6InhXazdJc1lQeTZrWkNvU2kiLCJzdWIiOjIzNzksInBydiI6ImI1NWUwZjA5NjQ4ZTkxZTk3ODEyNTA4M2VlMmNhZDUwMDk5YWMzZmYiLCJndWFyZCI6Im1hbGwiLCJvcmlnaW4iOiJcdTgyOWRcdTllYmJcdTc1MWZcdTYyMTAifQ.KO-RAAGSiCvk3885KCGbQb-9vvK2-1q6xV0PTprEdwI"
	// RobotLocationInfo, _ := service.OrderPoints{}.GetRobotLocationInfo(ctx, db, SalesID)
	RobotLocationInfo, _ := WinPoints{}.GetRobotLocationInfo1(ctx, db, SalesID)
	if RobotLocationInfo.PointsMallId != "" {
		// if "1" != "" {
		mallid = RobotLocationInfo.PointsMallId
		// mallid = "1"
		if _, ok := accessTokenMap[mallid]; ok {
			AccessToken = accessTokenMap[mallid]
		}
	}
	// return "3735", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hLnpoaW1hdGVjaC5jb21cL3Jlc3RmdWxcL21hbGxcLzM3MzVcL3Rva2VuIiwiaWF0IjoxNzA5ODYwMTg3LCJleHAiOjk1OTM4NjAxODcsIm5iZiI6MTcwOTg2MDE4NywianRpIjoiRzRLdFlZcE5JSjg4bGxFaCIsInN1YiI6MzczNSwicHJ2IjoiYjU1ZTBmMDk2NDhlOTFlOTc4MTI1MDgzZWUyY2FkNTAwOTlhYzNmZiIsImd1YXJkIjoibWFsbCIsIm9yaWdpbiI6Ilx1ODI5ZFx1OWViYlx1NzUxZlx1NjIxMCJ9.F4R19gE2UwJzNNlvGYMW-OesUn2nZWT5Al9o0I7j1U8"
	return
}

type WinDeviceLoginRsp struct {
	ErrorCode    string      `json:"errorCode"`
	ErrorMessage string      `json:"errorMessage"`
	ErrorCodeMsg interface{} `json:"errorCodeMsg"`
	Extra        interface{} `json:"extra"`
	Display      bool        `json:"display"`
	Exception    interface{} `json:"exception"`
	ReqTime      int         `json:"reqTime"`
	CostTime     int         `json:"costTime"`
	SkipType     interface{} `json:"skipType"`
	ServiceTime  int         `json:"serviceTime"`
	SkipParam    interface{} `json:"skipParam"`
	Body         WinBody     `json:"body"`
	Encrypted    bool        `json:"encrypted"`
}
type WinBody struct {
	WorkKey                string `json:"workKey"`
	AccessToken            string `json:"accessToken"`
	RegisterTime           int    `json:"registerTime"`
	SessionValiditySeconds int    `json:"sessionValiditySeconds"`
}

func NewWinPoints() PointsInterface {
	return &WinPoints{}
}

// 设备登陆
// 获取accessToken
func (WinPoints) GetAccessToken(ctx context.Context, SalesID string) (accessToken string, err error) {
	rdb := redis.NewDB()
	AccessToken, err1 := rdb.HGet(ctx, define.RedisWinPointsAccessToken, SalesID).Result()
	if AccessToken != "" && err1 == nil {
		return AccessToken, nil
	}

	// 重新登录
	rnd, _ := helper.UUID{}.String()
	var URL url.URL
	query := URL.Query()
	query.Add("deviceId", SalesID)
	query.Add("rnd", rnd)
	PostURL := config.Config.WinPoints.BaseURL + "/api/v3/securities/devices/register?" + query.Encode()

	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", PostURL, map[string]string{}, nil)
	var res WinDeviceLoginRsp
	json.Unmarshal(bytes, &res)
	logx.Error(ctx, "win devices register", logx.Any("url", PostURL), logx.Any("res", res), logx.Any("statusCode", statusCode), logx.Any("err", err))
	if statusCode != 200 || err != nil {
		return "", errorx.New("设备登录错误", -1)
	}
	if res.ErrorCode != WIN_SUCCESS {
		return "", errorx.New(res.ErrorMessage, -1)
	}
	//写入缓存
	rdb.HSet(ctx, define.RedisWinPointsAccessToken, SalesID, res.Body.AccessToken)
	rdb.Expire(ctx, define.RedisWinPointsAccessToken, time.Duration(res.Body.SessionValiditySeconds/2)*time.Second)
	return res.Body.AccessToken, nil
}

type WinCommonReqPre struct {
	Code    int    `json:"Code"`
	Message string `json:"Message"`
}

type WinCommonReq struct {
	ErrorCode    string      `json:"errorCode"`
	ErrorMessage string      `json:"errorMessage"`
	ErrorCodeMsg string      `json:"errorCodeMsg"`
	Extra        interface{} `json:"extra"`
	Display      bool        `json:"display"`
	Exception    interface{} `json:"exception"`
	ReqTime      int         `json:"reqTime"`
	CostTime     int         `json:"costTime"`
	SkipType     interface{} `json:"skipType"`
	ServiceTime  int         `json:"serviceTime"`
	SkipParam    interface{} `json:"skipParam"`
	Encrypted    bool        `json:"encrypted"`
}

// 构建请求头
func (WinPoints) buildHeader(access_token string) map[string]string {
	headers := make(map[string]string)
	headers["Content-Type"] = "application/json"

	headers["Authorization"] = access_token
	return headers
}

// 4.2 根据 【手机号】 查询会员资料
func (WinPoints) GetMemberInfo(ctx context.Context, db *gorm.DB, SalesID, phone, openid string) (
	data MemberMsg,
	err error,
) {
	// 获取mallid和access_token
	mallid, accessToken := WinPoints{}.GetSign(ctx, db, SalesID)
	URL := config.Config.WinPoints.BaseURL + "/restful/mall/" + mallid + "/member"
	queryURL := fmt.Sprintf(URL+"?mobile=%s", phone)
	header := WinPoints{}.buildHeader(accessToken)
	// statusCode 200代表网络请求成功
	statusCode, bytes, err := helper.Net{}.Request(ctx, "GET", queryURL, header, nil)
	var res WinMemberQueryRsp
	json.Unmarshal(bytes, &res)
	var rsp MemberMsg
	if statusCode != 200 || res.Code != 200 || err != nil {
		return rsp, errorx.New(res.Msg, int(res.Code))
	}
	rsp.NickName = res.Data.NickName
	rsp.Points = res.Data.Point
	rsp.HeadImg = res.Data.Avatar
	rsp.Phone = res.Data.Mobile
	rsp.ID = strconv.Itoa(res.Data.ID)
	rsp.CardNo = res.Data.Sn
	return rsp, nil
}

// 4.1 MemberRegist 会员注册 [ phone 必填 ]
func (WinPoints) MemberRegist(ctx context.Context, db *gorm.DB,
	SalesID, // 机器ID
	mallID, // 商场ID
	phone, // 只需要这个就行
	name string, // 姓名
	gender int, // 性别
	birthday, // 生日
	idCard, // 卡号
	Openid, // openid
	headImg, // 头像
	provinceId, // 省ID
	cityId, areaId, address string,
) (err error) {

	// 获取mallid和access_token
	mallid, accessToken := WinPoints{}.GetSign(ctx, db, SalesID)
	QueryURL := config.Config.WinPoints.BaseURL + "/restful/mall/" + mallid + "/member"
	// QueryURL := config.Config.WinPoints.BaseURL + "/restful/mall/" + config.Config.WinPoints.MallID + "/member"
	params := make(map[string]string)
	params["mobile"] = phone
	// 组装JSON字符串
	bytes, _ := json.Marshal(params) // 类似json_encode
	// 组装数组
	var PostData map[string]interface{}
	json.Unmarshal(bytes, &PostData) // 类似json_decode 变数组
	// sign := WinPoints{}.GetSign()
	header := WinPoints{}.buildHeader(accessToken)
	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, header, params)
	// 请求结构体
	var rsp WinMemberQueryRsp
	json.Unmarshal(bytes, &rsp)
	if statusCode != 200 || rsp.Code != 200 || err != nil {
		return errorx.New(rsp.Msg, int(rsp.Code))
	}
	return
}

type WinMallInfo struct {
	ID          string      `json:"id"`
	Code        string      `json:"code"`
	Name        string      `json:"name"`
	Icon        interface{} `json:"icon"`
	NameInitial interface{} `json:"nameInitial"`
	TypeID      interface{} `json:"typeId"`
	TypeName    interface{} `json:"typeName"`
}

type WinBalanceQueryRsp struct {
	WinCommonReq
	Body WinBalanceQueryBody `json:"body"`
}
type WinBalanceQueryBody struct {
	MemberID      string      `json:"memberId"`      //会员ID
	CardNo        string      `json:"cardNo"`        //会员卡号
	Point         string      `json:"point"`         //会员积分
	PointString   string      `json:"pointString"`   //会员积分
	Balance       int         `json:"balance"`       //会员储值余额，单位分
	BalanceString interface{} `json:"balanceString"` //会员储值余额
	CardType      string      `json:"cardType"`      // member -- 会员卡
}

// 4.3 PointsQuery 积分余额查询
func (WinPoints) PointsQuery(ctx context.Context, SalesID, openid, mallId string) (points float64, err error) {
	db := mysql.NewDB()
	var WechatUserInfo models.MaWechatUser
	var WechatUserCount int64
	db.Model(&models.MaWechatUser{}).
		Where("open_id = ?", openid).
		Take(&WechatUserInfo).
		Count(&WechatUserCount)
	if WechatUserCount == 0 || WechatUserInfo.Phone == "" {
		err = errorx.New("【赢商】当前用户未注册", 100099)
		return
	}
	// 验证正弘系统会否注册用户
	memberRsp, err := WinPoints{}.GetMemberInfo(ctx, db, SalesID, WechatUserInfo.Phone, WechatUserInfo.OpenID)
	if memberRsp.ID == "" || err != nil {
		err = errorx.New("【赢商】当前用户未注册", 100099)
		return
	}
	return memberRsp.Points, nil
}

// 4.4 BalanceTrade 积分余额支付
// SalesID             //必填 机器码
// cardNo              //必填 会员卡号
// tradeType           //必填 交易类型 recharge 增加 consume 扣减 revoke 冲正
// tradeAmount         //必填 交易数额 积分值 最多保留两位小数 (recharge 正数 consume 负数)
// clientTradeSn       //必填 客户端交易编号 不超过32位字符串
// originClientTradeSn //选填 原客户端交易编号 冲正时必填
// mallId              //条件可选，视现场需求而定
func (WinPoints) PointsConsume(ctx context.Context, db *gorm.DB,
	salesID, // 机器识别码
	openid, // openid
	clientTradeSn string,
	tradeAmount float64,
	mallId string,
) (
	err error,
) {
	var WechatUserInfo models.MaWechatUser
	var WechatUserCount int64
	mysql.NewDB().Model(&models.MaWechatUser{}).
		Where("open_id = ?", openid).
		Take(&WechatUserInfo).
		Count(&WechatUserCount)
	if WechatUserCount == 0 || WechatUserInfo.Phone == "" {
		err = errorx.New("【赢商】当前用户未注册", 100099)
		return
	}
	// 验证赢商系统会员是否注册用户
	memberRsp, err := WinPoints{}.GetMemberInfo(ctx, db, salesID, WechatUserInfo.Phone, WechatUserInfo.OpenID)
	if memberRsp.ID == "" || err != nil {
		err = errorx.New("【赢商】当前用户未注册", 100099)
		return
	}
	// 消费积分
	mallid, accessToken := WinPoints{}.GetSign(ctx, db, salesID)
	QueryURL := config.Config.WinPoints.BaseURL + "/restful/mall/" + mallid + "/point"
	var params = make(map[string]interface{})
	params["mobile"] = memberRsp.Phone  // 手机号
	params["type"] = 2                  // 积分用途定义：1.获得 2扣减 3过期
	params["change"] = -tradeAmount     // 扣除的积分
	params["origin"] = "线上消费积分抵现"       // 变动来源
	params["origin_id"] = clientTradeSn // 大道的内部订单号，调用方可追溯的唯一识别ID
	header := WinPoints{}.buildHeader(accessToken)
	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, header, params)
	// 请求结构体
	var rsp WinMemberQueryRsp
	// 定义赢商接口返回参数
	json.Unmarshal(bytes, &rsp)
	if statusCode != 200 || rsp.Code != 200 || err != nil {
		return errorx.New(rsp.Msg, int(rsp.Code))
	}
	logx.Info(
		ctx,
		"balance trade",
		logx.Any("queryURL", QueryURL),
		logx.Any("params", params),
		logx.Any("statusCode", statusCode),
		logx.Any("bytes", string(bytes)),
		logx.Any("err", err),
	)
	return nil
}

// 增加积分
// SalesID             //必填 机器码
// cardNo              //必填 会员卡号
// tradeType           //必填 交易类型 recharge 增加 consume 扣减 revoke 冲正
// tradeAmount         //必填 交易数额 积分值 最多保留两位小数 (recharge 正数 consume 负数)
// clientTradeSn       //必填 客户端交易编号 不超过32位字符串
// originClientTradeSn //选填 原客户端交易编号 冲正时必填
// mallId              //条件可选，视现场需求而定
func (WinPoints) PointsReverse(ctx context.Context, db *gorm.DB,
	salesID, // 机器识别码
	openid, //openID
	refundOrderID string, // 退款单号
	clientTradeSn string, // 订单号
	tradeAmount float64,
	mallId string,
) (
	err error,
) {
	// 查询微信用户是否存在
	var WechatUserInfo models.MaWechatUser
	var WechatUserCount int64
	mysql.NewDB().Model(&models.MaWechatUser{}).
		Where("open_id = ?", openid).
		Take(&WechatUserInfo).
		Count(&WechatUserCount)
	if WechatUserCount == 0 || WechatUserInfo.Phone == "" {
		err = errorx.New("【赢商】当前用户未注册", 100099)
		return
	}
	// 验证赢商系统会员是否注册用户
	memberRsp, err := WinPoints{}.GetMemberInfo(ctx, db, salesID, WechatUserInfo.Phone, WechatUserInfo.OpenID)
	if memberRsp.ID == "" || err != nil {
		err = errorx.New("【赢商】当前用户未注册", 100099)
		return
	}
	// 增加积分
	mallid, accessToken := WinPoints{}.GetSign(ctx, db, salesID)
	QueryURL := config.Config.WinPoints.BaseURL + "/restful/mall/" + mallid + "/point"
	var params = make(map[string]interface{})
	params["mobile"] = memberRsp.Phone  // 手机号
	params["type"] = 1                  // 积分用途定义：1.获得 2扣减 3过期
	params["change"] = tradeAmount      // 增加的积分
	params["origin"] = "退款返还抵扣积分"       // 变动来源
	params["origin_id"] = refundOrderID // 大道的内部退款订单号，调用方可追溯的唯一识别ID
	header := WinPoints{}.buildHeader(accessToken)
	statusCode, bytes, err := helper.Net{}.Request(ctx, "POST", QueryURL, header, params)
	// 请求结构体
	var rsp WinMemberQueryRsp
	// 定义赢商接口返回参数
	json.Unmarshal(bytes, &rsp)
	if statusCode != 200 || rsp.Code != 200 || err != nil {
		return errorx.New(rsp.Msg, int(rsp.Code))
	}
	return nil
}
